package seatiger.tomcat.util.net;

import seatiger.tomcat.util.StringManager;

import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;

/**
 * 处理tcp请求 使用一个线程等待连接，连接成功后将连接后的socket 放入线程池处理
 */
public class JIoEndpoint {

    protected StringManager sm = StringManager.getManager("seatiger.tomcat.util.net.res");

    /**
     * 是否初始化编制
     */
    protected boolean initialized = false;

    /**
     * server socket对象
     */
    protected ServerSocket serverSocket = null;

    protected ServerSocketFactory serverSocketFactory = null;

    protected int port;

    /**
     * 线程池 负责处理连接之后的socket
     */
    protected Executor executor = null;

    /**
     * 端口的运行状态
     */
    protected volatile boolean running = false;

    /**
     * 用于处理socket
     */
    protected Handler handler;

    public void setExecutor(Executor executor) {
        this.executor = executor;
    }

    public void setHandler(Handler handler) {
        this.handler = handler;
    }

    public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) {
        this.serverSocketFactory = serverSocketFactory;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * 初始化方法，负责创建serverSocket
     */
    public void init() throws Exception{
        if (initialized) {
            return;
        }
        if (serverSocketFactory == null) {
            serverSocketFactory = ServerSocketFactory.getDefault();
        }
        if (serverSocket == null) {
            try {
                serverSocket = serverSocketFactory.createSocket(port);
            }catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }
        initialized = true;
    }

    public void start() throws Exception {
        if (!initialized) {
            init();
        }
        if (!running) {
            running = true;
            Thread acceptorThread = new Thread(new Acceptor(),"-Acceptor-");
            acceptorThread.setDaemon(true);
            acceptorThread.start();
        }
    }

    public void destroy() {
        if (serverSocket != null) {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            serverSocket = null;
        }
        initialized = false;
    }

    protected class Acceptor implements Runnable {
        @Override
        public void run() {
            while (running) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
                try {
                    Socket socket = serverSocketFactory.acceptSocket(serverSocket);
                    if (!processSocket(socket)){
                        try {
                            socket.close();
                        } catch (IOException e) {
                        }
                    }
                }catch (Throwable t) {
                    //TODO
                    sm.getString("endpoint.accept.fail");
                }
            }
        }
    }

    /**
     * 处理连接之后socket
     * @param socket
     * @return
     */
    protected boolean processSocket(Socket socket) {
        try {
            if (executor != null) {
                executor.execute(new SocketProcessor(socket));
            }
        }catch (Throwable t) {
            //TODO
            sm.getString("endpoint.process.fail");
            return false;
        }
        return true;
    }

    protected class SocketProcessor implements Runnable{
        protected Socket socket;
        public SocketProcessor(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
            if (!handler.process(socket)){
                try {
                    socket.close();
                }catch (IOException e){}
            }
            socket = null;
        }
    }

    /**
     * 用于处理连接 后 获取到的socket
     */
    public interface Handler {
        boolean process(Socket socket);
    }
}
